package com.ilia.rubik;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import com.ilia.util.timer.ITimer;
import com.ilia.util.timer.TimerFactory;

public class Traverse {

	static String CMD_MAX_MOVES = "max-moves";
	static String CMD_OUT_FILE = "file.output";
	static String CMD_TMP_FILE = "file.temp";
	static String CMD_PRINT_DOTS = "print-dots";
	static String CMD_CUBE = "cube";
	static String CMD_INDICATOR = "indicator";
	static String CMD_INDICATOR_DEPTH = "indicator.depth";
	static String CMD_INIT_POS = "initial-position";
	static String CMD_FOUND_UNIQUE_MOVES = "found-unique-moves";
	static String CMD_FOUND_MOVES = "found-moves";
	static String CMD_TIMER_NAME = "timer.name";
	static String CMD_SEARCH_ALL = "search-all";
	static String CMD_OK_MOVES = "ok-moves";
	static String CMD_PAUSE = "pause";

	public static String nr = "\n";

	boolean doPause = false;
	int pause = 0;

	// int state_num =0;
	long last_dot_output_time = 0;
	long last_status_output_time = 0;
	long made_moves = 0;

	int max_moves = 0;
	char[] current_moves = null;
	int[] cube;
	int DOT_OUTPUT_PERIOD = 10000;
	int STATUS_OUTPUT_PERIOD = 30000;
	int[] target_cube;
	int[] init_cube;
	Map<String,int[]> indicators = new HashMap<String,int[]>();
	TraverseIndicator traverseIndicator;
	int indicator_depth = -1;
	List<String> foundMoves;
	boolean solutionFounded = false;
	boolean printDots = false;
	boolean immediatelyExit = false;
	char[] immediatelyExitPosition = null;
	int maxSolutionMoves = 0;

	String outputName = null;
	String tmpName = null;
	String initialPosition = null;
	String timerClassName = null;
	boolean searchAll = true;
	Map<String,String> derivablePositions;
	ITimer timer;
	PossibleMoves pm;
	
	long started_at, finished_at;
	

	
	boolean stateOK() {
		timer.startTimer("CUBE_EQ");
		boolean bool = Cube.equals(target_cube, cube);
		timer.stopTimer("CUBE_EQ");
		return bool;
	}

	boolean isUp() {
		timer.startTimer("IS_UP");
		boolean result = isUp_internal();
		timer.stopTimer("IS_UP");
		return result;
	}
	
	boolean isUp_internal() {
		if (immediatelyExit) return false;
		if (current_moves.length >= max_moves) return false;
		if (solutionFounded && !searchAll) return false;
		if (stateOK()) return false;
		if (!traverseIndicator.cube_can_be_moved_to_target(cube, max_moves-current_moves.length)) return false;
		return true;
	}

	public Traverse(int[] init_cube, int[] target_cube, int max_moves) {
		this.target_cube = Cube.copy(target_cube);
		this.init_cube = Cube.copy(init_cube);
		this.max_moves = max_moves;
	}

	public Traverse(String fileName) {
		configure(fileName);
	}

	private String[] parseFileName(String filePath) {
		File f = new File(filePath);
		String ap = f.getAbsolutePath();
		String fileName = f.getName();
		File parentFile = f.getParentFile();
		String parent = parentFile.getAbsolutePath();
		
		String name = null;
		String ext = null;
		int dotIndex = fileName.indexOf('.');
		if (dotIndex > 1) {
			name = fileName.substring(0, dotIndex);
			ext = fileName.substring(dotIndex + 1, fileName.length());
		}

		String[] str = new String[4];
		str[0] = ap;
		str[1] = parent;
		str[2] = name;
		str[3] = ext;
		return str;
	}

	boolean isRight() {
		if (immediatelyExit) return false;
		if (solutionFounded && !searchAll) return false;
		char lastMove = current_moves[current_moves.length - 1];
		char[] possibleNextMoves = pm.cachedPossibleRightMoves(current_moves, current_moves.length - 1);
		if (lastMove == possibleNextMoves[possibleNextMoves.length - 1]) return false;
		return true;
	}

	boolean isDown() {
		return (current_moves.length > 0);
	}

	void up() {
		char[] new_moves = Cube.copyChars(current_moves, 0, current_moves.length + 1);
		char nextMove = pm.cachedPossibleRightMoves(current_moves, current_moves.length)[0];
		new_moves[new_moves.length - 1] = nextMove;
		current_moves = new_moves;
		timer.startTimer("ROTATE");
		Cube.move(nextMove, cube,cube);
		timer.stopTimer("ROTATE");
	}

	static String print_chars(String name, char[] chars) {
		StringBuffer sb = new StringBuffer();
		sb.append(name + "=[");
		for (int i = 0; i < chars.length; i++) {
			sb.append(chars[i]);
		}
		sb.append("]");
		return sb.toString();
	}

	void right() {
		timer.startTimer("RIGHT");
		char lastMove = current_moves[current_moves.length - 1];
		
		timer.startTimer("ROTATE");
		Cube.undo_move(lastMove, cube,cube);
		timer.stopTimer("ROTATE");

		char nextMove =' ';
		char[] possibleNextMoves = pm.cachedPossibleRightMoves(current_moves, current_moves.length-1);
		for (int i=0;i<possibleNextMoves.length-1;i++) {
			if (possibleNextMoves[i]==lastMove) {
				nextMove = possibleNextMoves[i+1];
				break;
			}
		}

		current_moves[current_moves.length - 1] = nextMove;

		timer.startTimer("ROTATE");
		Cube.move(nextMove, cube,cube);
		timer.stopTimer("ROTATE");
		timer.stopTimer("RIGHT");

	}

	void down() {
		char lastMove = current_moves[current_moves.length - 1];
		timer.startTimer("ROTATE");
		Cube.undo_move(lastMove, cube,cube);
		timer.stopTimer("ROTATE");
		current_moves = Cube.copyChars(current_moves, 0, current_moves.length - 1);
	}

	private String moveIsUnique(String move) {
		timer.startTimer("UNIQUE");
		int[] cube = Cube.create_complete_cube();
		timer.stopTimer("UNIQUE");

		timer.startTimer("ROTATE");
		int[] res1 = Cube.create_empty_cube(); 
		Cube.move(move, cube, res1);
		timer.stopTimer("ROTATE");

		timer.startTimer("UNIQUE");
		String foundMove = (String) derivablePositions.get(Cube.cube_to_hash_string(res1));
		if (foundMove == null) {
			derivablePositions.put(Cube.cube_to_hash_string(res1), move);
			timer.stopTimer("UNIQUE");
			return null;
		}
		timer.stopTimer("UNIQUE");
		return foundMove;
	}

	void UV() {
		timer.startTimer("UV");
		while (isUp()) {
			up();
			work();
		}
		timer.stopTimer("UV");
	}

	void init() {
		started_at = System.currentTimeMillis();

		if (initialPosition != null) {
			current_moves = initialPosition.toCharArray();
			cube = Cube.copy(init_cube);
			Cube.move(current_moves, cube,cube);

		} else {
			current_moves = new char[0];
			cube = Cube.copy(init_cube);
		}

		solutionFounded = false;

		// state_num = 0;
		derivablePositions = new HashMap<String,String>();
		last_dot_output_time = System.currentTimeMillis();
		last_status_output_time = System.currentTimeMillis();
		if (foundMoves == null)
			foundMoves = new ArrayList<String>();

		System.out.println(Cube.cube_to_string(
				new String[] { "init", "target" }, new int[][] { init_cube,
						target_cube }, 5));
		pm = new PossibleMoves(timer);
		pm.computePRM();
		
		timer.startTimer("IND_ALL");

		traverseIndicator = new TraverseIndicator();
		if (!indicators.isEmpty()) {
			traverseIndicator.configure(indicator_depth, pm, target_cube, indicators, timer);
			traverseIndicator.traverse();
		}
		timer.stopTimer("IND_ALL");
		

	}

	private String commandString(String cmd, String value) {
		return cmd + "=" + value;
	}

	private void userCommand(byte command) {
		char c = (char) command;
		switch (c) {
		case 'e':
			immediatelyExit = true;
			immediatelyExitPosition = current_moves;
			break;
		case 'p':
			System.out.println(statusToString(true));
			break;
		case 'd':
			printDots = !printDots;
			System.out.println("dots is " + (printDots ? "on" : "off"));
			break;
		case ' ':
			doPause = true;
			break;
		case '?':
		case 'h':
			System.out
					.println("e - exit, p - print, d - on/off dots, SPACE-pause, ?,h - this screen");
			break;

		}
	}

	void work() {
		made_moves++;
		if (System.currentTimeMillis() - DOT_OUTPUT_PERIOD >= last_dot_output_time) {
			last_dot_output_time = System.currentTimeMillis();
			
			if (System.currentTimeMillis() - DOT_OUTPUT_PERIOD >= last_status_output_time) {
				last_status_output_time = System.currentTimeMillis();
				String shortest = getShortestMove();
				System.out.println("shortest-move="+shortest+" found="+foundMoves.size()+ " made-moves="+made_moves+" current-move="+new String(current_moves));
			}
			
			
			try {
				byte[] bytes = new byte[1];
				int av = System.in.available();
				if (av > 0) {
					int read2 = System.in.read(bytes);
					if (read2 > 0) {
						userCommand(bytes[0]);
					}
					System.in.skip(System.in.available());
				}
			} catch (IOException e) {
			}

		}
		if (doPause) {
			System.out.println("pause. press ENTER to continue");
			try {
				System.in.skip(System.in.available());
			} catch (IOException e) {
			}
			doPause = false;

		}
		if (pause > 0) {
			try {
				Thread.yield();
				Thread.sleep(pause);
			} catch (InterruptedException e) {
			}
		}

		if (stateOK()) {
			timer.startTimer("ADD_MOVE");
			String myMove = new String(current_moves);
			String dupMove = null;// moveIsUnique(myMove);
			if (dupMove == null) {
				foundMoves.add(myMove);
				if (maxSolutionMoves >= myMove.length())
					solutionFounded = true;
			} 
			/*else {
				if (dupMove.length() > myMove.length()) {
					foundMoves.remove(dupMove);
					foundMoves.add(myMove);
					if (maxSolutionMoves >= myMove.length())
						solutionFounded = true;
				}
			}
			*/
			timer.stopTimer("ADD_MOVE");
		}

	}

	private String foundMovesToString(Collection<String> moves) {
		int wrap = 0;
		int wrap_by = 4;
		StringBuffer sb = new StringBuffer();
		for (String move : moves) {
			sb.append(move);
			wrap++;
			if (wrap==wrap_by) {
				wrap = 0;
				sb.append("\n");
			} else {
				sb.append(" ");
			}
		}
		return sb.toString();
	}

	
	private String getShortestMove() {
		String shortest = null;
		for (int i = 0; i < foundMoves.size(); i++) {
			String move = foundMoves.get(i);
			if ((shortest == null) || (move.length() < shortest.length())) {
				shortest = move;
			}
		}
		return shortest;
	}
	
	private String statusToString(boolean tmp) {
		StringBuffer sb = new StringBuffer();
		long total_time = (finished_at-started_at)/1000;
		sb.append(Cube.cube_to_string(new String[] { "init", "target" },
				new int[][] { init_cube, target_cube }, 5));
		sb.append(nr);

		if (tmp) {
			if (immediatelyExitPosition != null) {
				sb.append(commandString(CMD_INIT_POS, ""
						+ new String(immediatelyExitPosition))
						+ nr);
			} else {
				sb.append(commandString(CMD_INIT_POS, ""
						+ new String(current_moves))
						+ nr);
			}
		}
		sb.append("time= "+total_time+" sec"+nr);
		sb.append("timer= "+timer.toString()+nr);
		sb.append(commandString(CMD_MAX_MOVES, "" + max_moves) + nr);
		sb.append(commandString(CMD_INDICATOR_DEPTH, "" + indicator_depth) + nr);
		sb.append(commandString(CMD_INDICATOR, "" + indicators.keySet().iterator().next()) + nr);
		sb.append(commandString(CMD_SEARCH_ALL, "" + searchAll) + nr);
		sb.append(commandString(CMD_OUT_FILE, "" + outputName) + nr);
		
		

		Set<String> uniqueMoves = new HashSet<String>();
		for (String move : foundMoves) {
			String um = move;
			boolean changed = true;
			while (changed) {
				String newmove = normalizeIndependentMove(um);
				if (um.equals(newmove)) {
					changed = false;
				}
				um = newmove;
			}
			um = doubleToUpperCase(um);
			uniqueMoves.add(um);
		}
		
		
		if (!tmp) {
			String shortest = getShortestMove();
			sb.append(commandString("shortest-move", shortest + nr));
		}
		
		List<String> sortedMoves = new ArrayList<String>(uniqueMoves.size());
		sortedMoves.addAll(uniqueMoves);
		Collections.sort(sortedMoves, new MoveComparator());
		
//		sb.append(commandString(CMD_FOUND_MOVES, ""+ foundMoves.size())+ nr);
		sb.append(commandString(CMD_FOUND_UNIQUE_MOVES, ""+ sortedMoves.size())+ nr);
		sb.append(foundMovesToString(sortedMoves));
		sb.append(nr);
/*
		if (!tmp) {
			for (int i = 0; i < foundMoves.size(); i++) {
				String move = (String) foundMoves.get(i);
				int[] cube = Cube.create_complete_cube();

				
				int[] result1 = Cube.create_empty_cube();
				int[] result2 = Cube.create_empty_cube();

				Cube.move(Cube.inverse_move(move), cube, result1 );
				Cube.move(move, cube, result2);

				sb.append(Cube.cube_to_string(new String[] {
						"(" + move + ")-1", "", "" + move + "" }, new int[][] {
						result1, cube, result2 }, 5));
				sb.append(nr);
			}
		}
*/
		return sb.toString();
	}
	
	private class MoveComparator implements Comparator<String> {

		@Override
		public int compare(String o1, String o2) {
			int l1 = o1.length();
			int l2 = o2.length();
			if (l1<l2) return -1;
			if (l1>l2) return 1;
			int magic =  (magicFormel(o1)-magicFormel(o2));
			if (magic!=0) {
				return magic;
			}
			return o1.compareTo(o2);
		}
		
		private int magicFormel(String move) { // the less the better
			int l  = move.length();
			int p = l + sideNumber(move);
			p = p - doubleNumber(move);
			return p;
		}

		private int sideNumber(String move) {
			Set<String> sides = new HashSet<String>();
			for (int i = 0; i < move.length(); i++) {
				String s = ""+move.charAt(i);
				sides.add(s.toUpperCase());
			}
			return sides.size();
		}

		private int doubleNumber(String move) {
			int doubles = 0;
			for (int i = 0; i < move.length()-1; i++) {
				char ch1 = move.charAt(i);
				char ch2 = move.charAt(i+1);
				if (ch1==ch2) {
					doubles++;
				}
			}
			return doubles;
		}

		
	}
	
	private String normalizeIndependentMove(String m) {
		char move[] = new char[m.length()];
		m.getChars(0, m.length(), move, 0);
		for (int i = 0; i < move.length-1; i++) {
			char ch1 = move[i];
			char ch2 = move[i+1];
			char[] im = isIndependentMove(ch1,ch2);
			if (im!=null) {
				move[i] = im[0];
				move[i+1] = im[1];
			}
		}
		return new String(move);		
	}
	
	
	private String normalizeTripleMove(String m) {
		// FFF -> f
		char move[] = new char[m.length()];
		m.getChars(0, m.length(), move, 0);
		for (int i = 0; i < move.length-2; i++) {
			char ch1 = move[i];
			char ch2 = move[i+1];
			char ch3 = move[i+2];
			
			char chu1 = Character.toUpperCase(ch1);
			
			
			if ((ch1==ch2)&&(ch1==ch3)) {
				char normal_ch = ch1;
				if (ch1==chu1) {
					normal_ch = Character.toLowerCase(ch1);
				} else {
					normal_ch = Character.toUpperCase(ch1);
				}
			}
		}
		return new String(move);		
	}	
	
	
	private List<char[]> INDEP_PAIRS = null;
	
	
	
	private char[] isIndependentMove(char ch1, char ch2) {
		char[] indep_move = null; 
		if (INDEP_PAIRS==null) {
			//LR
			//TD
			//FB
			INDEP_PAIRS = new ArrayList<char[]>();
			char [] pair = new char[2];
			pair[0] = 'L';
			pair[1] = 'R';
			INDEP_PAIRS.add(pair);
			
			pair = new char[2];
			pair[0] = 'T';
			pair[1] = 'D';
			INDEP_PAIRS.add(pair);
			
			pair = new char[2];
			pair[0] = 'F';
			pair[1] = 'B';
			INDEP_PAIRS.add(pair);
		}
			
		char chu1 = Character.toUpperCase(ch1);
		char chu2 = Character.toUpperCase(ch2);
		
		
		for (char[] c : INDEP_PAIRS) {
			
			if  ((chu1==c[0])&&(chu2==c[1]))  {
				indep_move = new char[2];
				indep_move[0] = ch1;
				indep_move[1] = ch2;
				
			}
			if  ((chu1==c[1])&&(chu2==c[0]))  {
				indep_move = new char[2];
				indep_move[0] = ch2;
				indep_move[1] = ch1;
			}			
			
		}
		
		return indep_move;
	}
	
	private String doubleToUpperCase(String m) {
		char move[] = new char[m.length()];
		m.getChars(0, m.length(), move, 0);
		for (int i = 0; i < move.length-1; i++) {
			char ch1 = move[i];
			char ch2 = move[i+1];
			if (ch1==ch2) {
				move[i] = Character.toUpperCase(ch1);
				move[i+1] = Character.toUpperCase(ch2);
			}
		}
		return new String(move);
	}
	
	public void finish(boolean tmp, String filename) {
		finished_at = System.currentTimeMillis();
		String s = statusToString(tmp);
		save(filename, s);
		System.out.println(s);
	}

	public void save(String fileName, String content) {
		try {
			FileWriter fw = new FileWriter(fileName);
			BufferedWriter bw = new BufferedWriter(fw);
			bw.write(content);
			bw.flush();
			bw.close();
		} catch (IOException ex) {
			System.out.println("error while saving file");
		}

	}

	void traverse() {
		timer = TimerFactory.createTimer(timerClassName);
		timer.startTimer("ALL");
		init();
		work();
		UV();
		while (isDown()) {
			if (isRight()) {
				right();
				work();
				UV();
			} else {
				down();
				work();
			}
		}
		timer.stopTimer("ALL");
		if (immediatelyExit) {
			finish(true, tmpName);
		} else {
			finish(false, outputName);
		}

	}

	public void search(char[] currentState, char[] moves, int level) {
	}

	String moves_to_string(char[] chars) {
		StringBuffer sb = new StringBuffer();
		sb.append(new String(chars));
		return sb.toString();

	}




	public static void main(String[] args) {

		if (args.length == 1) {
			Traverse t = new Traverse(args[0]);
			t.traverse();
		} else {
			System.out.println("specify config file as parameter!");
		}

	}
	
	private String read_indicator(String name) {
		StringBuffer sb = new StringBuffer();
		String resourceName = "com/ilia/rubik/"+name+"-indicator.txt";
		InputStream is = this.getClass().getClassLoader().getResourceAsStream(resourceName);
		BufferedInputStream bis = new BufferedInputStream(is);
		try {
			while (bis.available()>0) {
				byte[] bytes = new byte[bis.available()];
				bis.read(bytes, 0 , bytes.length);
				String s = new String(bytes);
				sb.append(s);
			}
		} catch (IOException ex) {
			ex.printStackTrace();
		} finally {
			try {
				bis.close();
			} catch (IOException ex1) {
				ex1.printStackTrace();
			}
		}
		return sb.toString();
	}
	
	private void configureIndicator(String indicatorName) {
		String indicatorValue = read_indicator(indicatorName);
		StringTokenizer st = new StringTokenizer(indicatorValue, nr);
		boolean cubeRecords = false;
		List<String> cubeRecordsContent=new ArrayList<String>();
		boolean cubeInitMode = false;
		boolean cubeIndicatorMode = false;
		
		while (st.hasMoreElements()) {
			String str = st.nextToken();
			if (cubeRecords) {
				if (cubeRecordsContent.size()==16) {
					cubeRecordsContent.add(str);
					int[][] cubes = Cube.read_cube(cubeRecordsContent);
					if (cubeInitMode) {
						this.init_cube = cubes[0];
						this.target_cube = cubes[1];
					} else if (cubeIndicatorMode) {
						indicators.put(indicatorName,cubes[0]);
					}
					
					cubeRecords = false;
					cubeRecordsContent.clear();
					cubeInitMode = false;
					cubeIndicatorMode = false;
				} else {
					cubeRecordsContent.add(str);
					
					
				}
			} else {
				StringTokenizer st2 = new StringTokenizer(str, "=");
				String name = null;
				String value = null;
				while (st2.hasMoreTokens()) {
					String token = st2.nextToken();
					if (name == null) {
						name = token.trim();
					} else if (value == null) {
						value = token.trim();
					}
				} 
				if ((name != null) && (value != null)) {
					if (CMD_INDICATOR.equalsIgnoreCase(name)) {
						cubeRecords = true;
						cubeIndicatorMode = true; 
					}
				}				
			}
		}
		
	}
	
	public void configure(String fileName) {
		String config = readConfigFile(fileName);
		String[] configPathes = parseFileName(fileName);
		String parentFile = configPathes[1];
		outputName = parentFile + File.separator + configPathes[2] + ".result.txt";
		tmpName = parentFile + File.separator + configPathes[2] + ".tmp";

		StringTokenizer st = new StringTokenizer(config, nr);
		timerClassName = "com.ilia.rubik.EmptyTimer";
		searchAll = true;
		
		boolean cubeRecords = false;
		boolean cubeInitMode = false;
		String indicatorName = "complete-cube";
		
		
		List<String> cubeRecordsContent=new ArrayList<String>();
		
		while (st.hasMoreElements()) {
			String str = st.nextToken();
			
			if (cubeRecords) {
				if (cubeRecordsContent.size()==16) {
					cubeRecordsContent.add(str);
					int[][] cubes = Cube.read_cube(cubeRecordsContent);
					if (cubeInitMode) {
						this.init_cube = cubes[0];
						this.target_cube = cubes[1];
					}
					cubeRecords = false;
					cubeRecordsContent.clear();
					cubeInitMode = false;
				} else {
					cubeRecordsContent.add(str);
					
					
				}
			} else {

				StringTokenizer st2 = new StringTokenizer(str, "=");
				String name = null;
				String value = null;
				while (st2.hasMoreTokens()) {
					String token = st2.nextToken();
					if (name == null) {
						name = token.trim();
					} else if (value == null) {
						value = token.trim();
					}
				} 
				if ((name != null) && (value != null)) {
					if (CMD_MAX_MOVES.equalsIgnoreCase(name)) {
						max_moves = Integer.parseInt(value);
						if (maxSolutionMoves == 0) {
							maxSolutionMoves = max_moves;
						}
					} else if (CMD_OK_MOVES.equalsIgnoreCase(name)) {
						maxSolutionMoves = Integer.parseInt(value);
					} else if (CMD_OUT_FILE.equalsIgnoreCase(name)) {
						outputName = value;
					} else if (CMD_PRINT_DOTS.equalsIgnoreCase(name)) {
						if ("yes".equalsIgnoreCase(value)) {
							printDots = true;
						}
					} else if (CMD_INIT_POS.equalsIgnoreCase(name)) {
						initialPosition = value;
					} else if (CMD_FOUND_MOVES.equalsIgnoreCase(name)) {
						foundMoves = splitBySpace(value);
					} else if (CMD_TIMER_NAME.equalsIgnoreCase(name)) {
						timerClassName = value;
					} else if (CMD_SEARCH_ALL.equalsIgnoreCase(name)) {
						if ("yes".equalsIgnoreCase(value)) {
							searchAll = true;
						}
					} else if (CMD_PAUSE.equalsIgnoreCase(name)) {
						pause = Integer.parseInt(value);
					} else if (CMD_CUBE.equalsIgnoreCase(name)) {
						cubeRecords = true;
						cubeInitMode = true;
					} else if (CMD_INDICATOR.equalsIgnoreCase(name)) {
						indicatorName = value;
					} else if (CMD_INDICATOR_DEPTH.equalsIgnoreCase(name)) {
						indicator_depth = Integer.parseInt(value);
					}
				}
			}

				
		}
		if (indicator_depth<0) {
			indicator_depth = ( max_moves +1 ) / 2;
		}
		
		configureIndicator(indicatorName);

	}

	private List<String> splitBySpace(String str) {
		List<String> res = new ArrayList<String>();
		StringTokenizer st = new StringTokenizer(str, " ");
		while (st.hasMoreTokens()) {
			res.add(st.nextToken());
		}
		return res;
	}

	public String readConfigFile(String file_name) {
		try {
			BufferedReader in = new BufferedReader(new FileReader(file_name));
			StringBuffer sb = new StringBuffer();
			String ins = in.readLine();
			while (ins != null) {
				sb.append(ins);
				sb.append(nr);
				ins = in.readLine();
			}
			String config = sb.toString();
			in.close();
			return config;
		} catch (FileNotFoundException ex) {
			System.out.println("file not found " + file_name);
		} catch (IOException ex1) {
			System.out.println("error reading input file " + file_name);
		}
		return null;

	}

}
